/*
 * Copyright (c) 2001-2007, Inversoft, All Rights Reserved
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 */
package org.inversoft.vertigo.servlet;

import java.io.IOException;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.inversoft.vertigo.jpa.EntityManagerContext;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import net.java.lang.StringTools;

/**
 * <p>
 * This is the main Servlet filter for the Vertigo framework. This
 * will setup the {@link ServletContextHolder}, the {@link EntityManagerContext}
 * and any other Vertigo configuration that is needed.
 * </p>
 *
 * @author  Brian Pontarelli
 */
public class VertigoFilter implements Filter {
    private static Logger logger = Logger.getLogger(VertigoFilter.class.getName());

    private String persistentUnit = "punit";
    private EntityManagerFactory emf;

    /**
     * First this adds the ServletContext to the holder so that is available anywhere in the web
     * application. Next this checks for the <em>persistentUnit</em> configuration option and defaults
     * to <em>punit</em>. This also constructs the EntityManagerFactory that will be used to create
     * the EntityManagers from.
     *
     * @param   filterConfig The filter config to get the init params from.
     */
    public void init(FilterConfig filterConfig) throws ServletException {
        ServletContextHolder.setServletContext(filterConfig.getServletContext());

        String punit = filterConfig.getInitParameter("persistentUnit");
        if (punit != null) {
            persistentUnit = punit;
        }

        emf = Persistence.createEntityManagerFactory(persistentUnit);

        // setup guice and set it into the servlet context
        initGuice(filterConfig);
    }

    private void initGuice(FilterConfig filterConfig) throws ServletException {
        boolean initGuice = false;
        String initGuiceParam = filterConfig.getInitParameter("initGuice");
        if (!StringTools.isEmpty(initGuiceParam)) {
            try {
                initGuice = new Boolean(initGuiceParam).booleanValue();
            } catch (Exception e) {
                // stub, initGuice defaults to false
            }
        }

        if (initGuice) {
            String moduleClassParam = filterConfig.getInitParameter("guiceModule");
            if (!StringTools.isEmpty(moduleClassParam)) {
                try {
                    Class moduleClass = Class.forName(moduleClassParam);

                    if (Module.class.isAssignableFrom(moduleClass)) {
                        logger.finest("VertigoFilter: Creating with module [" + moduleClass + "]");
                        Module module = (Module) moduleClass.newInstance();
                        Injector injector = Guice.createInjector(module);
                        ServletContextHolder.getServletContext().setAttribute("guiceInjector", injector);
                    }
                } catch (ClassNotFoundException e) {
                    throw new ServletException(e);
                } catch (IllegalAccessException e) {
                    throw new ServletException(e);
                } catch (InstantiationException e) {
                    throw new ServletException(e);
                }
            }
        }
    }

    /**
     * Creates an EntityManager, stores it in the context and then clears the context in a finally
     * block.
     *
     * @param   request Passed down chain.
     * @param   response Passed down chain.
     * @param   chain The chain.
     * @throws  IOException If the chain throws an exception.
     * @throws  ServletException If the chain throws an exception.
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {
        EntityManager entityManager = emf.createEntityManager();
        try {
            // Grab the EntityManager for this request. This is lightweight so don't freak out that
            // this will use resources. It doesn't grab a JDBC connection until it has to.
            EntityManagerContext.set(entityManager);

            // Proceed down the chain
            chain.doFilter(request, response);
        } finally {
            // Clear out the context just to be safe.
            EntityManagerContext.remove();
            entityManager.close();
        }
    }

    /**
     * Closes the EntityManagerFactory and removes the context from the holder.
     */
    public void destroy() {
        ServletContextHolder.setServletContext(null);
        emf.close();
    }
}